package org.piggy.client.support.spring;

import lombok.extern.slf4j.Slf4j;
import org.piggy.client.core.scanner.ApiAnnotationScanner;
import org.piggy.client.autoconfigure.ApiProperties;
import org.piggy.client.support.AbstractClientRegisterManager;
import org.piggy.common.config.ServiceDefinition;
import org.piggy.common.config.ServiceInstance;
import org.piggy.common.constants.BasicConst;
import org.piggy.common.utils.NetUtil;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import javax.annotation.Nonnull;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

@Slf4j
public class SpringClientRegisterManager extends AbstractClientRegisterManager implements ApplicationListener<ApplicationEvent>, ApplicationContextAware {

    private final Set<Object> set = new HashSet<>();

    private ApplicationContext applicationContext;

    private ServerProperties serverProperties;

    @Autowired
    public void setServerProperties(ServerProperties serverProperties) {
        this.serverProperties = serverProperties;
    }

    public SpringClientRegisterManager(ApiProperties apiProperties) {
        super(apiProperties);
    }

    @Override
    public void setApplicationContext(@Nonnull ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public void onApplicationEvent(@Nonnull ApplicationEvent applicationEvent) {
        if (applicationEvent instanceof ApplicationStartedEvent) {
            doRegisterSpringMvc();
            log.info("springmvc api started");
        }
    }

    private void doRegisterSpringMvc() {

        Map<String, RequestMappingHandlerMapping> allRequestMappings =
                BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, RequestMappingHandlerMapping.class, true, false);

        for (RequestMappingHandlerMapping handlerMapping : allRequestMappings.values()) {

            Map<RequestMappingInfo, HandlerMethod> handlerMethods = handlerMapping.getHandlerMethods();

            for (Map.Entry<RequestMappingInfo, HandlerMethod> method : handlerMethods.entrySet()) {
                //获取方法
                HandlerMethod handlerMethod = method.getValue();

                Object beanName = handlerMethod.getBean();

                Class<?> clazz = handlerMethod.getBeanType();
                Object bean = applicationContext.getBean(clazz);

                if (set.contains(bean)) {
                    continue;
                }

                ServiceDefinition serviceDefinition = ApiAnnotationScanner.getInstance().scanner(bean);

                if (serviceDefinition == null) {
                    continue;
                }

                ServiceInstance serviceInstance = new ServiceInstance();
                int port = serverProperties.getPort();
                String localIp = NetUtil.getLocalIp();
                String serviceInstanceId = localIp + BasicConst.COLON_SEPARATOR + port;
                String serviceName = serviceDefinition.getServiceName();
                String version = serviceDefinition.getVersion();

                serviceInstance.setServiceInstanceId(serviceInstanceId);
                serviceInstance.setServiceName(serviceName);
                serviceInstance.setIp(localIp);
                serviceInstance.setPort(port);
                serviceInstance.setRegisterTime(System.currentTimeMillis());
                serviceInstance.setVersion(version);
                serviceInstance.setWeight(getApiProperties().getWeight());
                serviceDefinition.setEnvType(getApiProperties().getEnv());
                if (getApiProperties().isGray()){
                    serviceInstance.setGray(true);
                }

                register(serviceDefinition, serviceInstance);
                set.add(bean);
            }
        }
    }
}
